"""
日志记录工具模块
提供统一的日志记录功能，支持按日期和级别分割日志文件
支持按大小自动轮转日志文件
"""
import logging
import os
import fcntl
from datetime import datetime
from logging.handlers import TimedRotatingFileHandler, RotatingFileHandler
from pathlib import Path

# 日志目录 - 使用项目根目录的runtime
LOG_DIR = Path(__file__).parent.parent.parent / "runtime" / "logs"
LOG_DIR.mkdir(parents=True, exist_ok=True)

# 日志格式
LOG_FORMAT = "[%(asctime)s] [%(levelname)s] [%(filename)s:%(lineno)d] %(message)s"
DATE_FORMAT = "%Y-%m-%d %H:%M:%S"

# 日志文件大小限制（2MB）
MAX_LOG_SIZE = 2 * 1024 * 1024  # 2MB

# 全局logger字典，避免重复创建
_loggers = {}


class SizeRotatingFileHandler(RotatingFileHandler):
    """
    按大小分割的日志处理器
    文件名格式：<level>.log
    达到大小限制时重命名为：<年月日>/<level>_<时分秒>.log
    """
    def emit(self, record):
        """
        Emit a record.
        Process-safe implementation of log rotation.
        """
        lock_path = self.baseFilename + ".lock"
        lock_file = None
        try:
            lock_file = open(lock_path, "w")
            # Acquire an exclusive, blocking lock
            fcntl.flock(lock_file, fcntl.LOCK_EX)

            # Now we have the lock, we can safely check and rotate
            if self.shouldRollover(record):
                self.doRollover()
            
            logging.FileHandler.emit(self, record)

        except Exception:
            self.handleError(record)
        finally:
            if lock_file:
                fcntl.flock(lock_file, fcntl.LOCK_UN)
                lock_file.close()
                try:
                    if os.path.exists(lock_path):
                        os.remove(lock_path)
                except OSError:
                    pass

    def __init__(self, log_dir, level_name, max_bytes=MAX_LOG_SIZE, backup_count=30):
        """
        初始化日志处理器
        
        Args:
            log_dir: 日志目录
            level_name: 日志级别名称（debug/info/error/sync）
            max_bytes: 文件大小限制，默认2MB
            backup_count: 保留的备份文件数量，默认30个
        """
        self.log_dir = Path(log_dir)
        self.level_name = level_name.lower()
        self.max_bytes = max_bytes
        
        # 生成当前日志文件名
        filename = self._generate_filename()
        
        super().__init__(
            filename=str(filename),
            maxBytes=max_bytes,
            backupCount=backup_count,
            encoding='utf-8'
        )
        
    def _generate_filename(self):
        """生成日志文件名：<level>.log"""
        return self.log_dir / f"{self.level_name}.log"
    
    def doRollover(self):
        """
        执行日志文件轮转
        重写父类方法，使用自定义的文件命名规则
        当文件达到大小限制时，重命名为：<年月日>/<level>_<时分秒>.log
        """
        if self.stream:
            self.stream.close()
            self.stream = None
        
        # 生成日期子目录和备份文件名
        date_str = datetime.now().strftime("%Y%m%d")
        time_str = datetime.now().strftime("%H%M%S")
        
        # 创建日期子目录
        date_dir = self.log_dir / date_str
        date_dir.mkdir(parents=True, exist_ok=True)
        
        # 备份文件名：<年月日>/<level>_<时分秒>.log
        backup_filename = date_dir / f"{self.level_name}_{time_str}.log"
        
        # 如果当前日志文件存在，移动到备份目录
        if os.path.exists(self.baseFilename):
            # 确保备份文件名不重复
            counter = 0
            original_backup = backup_filename
            while os.path.exists(str(backup_filename)):
                counter += 1
                backup_filename = date_dir / f"{self.level_name}_{time_str}_{counter}.log"
            
            os.rename(self.baseFilename, str(backup_filename))
        
        # 删除过期的备份文件（保留最新的backupCount个）
        self._cleanup_old_backups()
        
        # 打开新文件
        if not self.delay:
            self.stream = self._open()
    
    def _cleanup_old_backups(self):
        """清理过期的备份文件，保留最新的backupCount个"""
        if self.backupCount <= 0:
            return
        
        # 获取所有日期子目录中的备份文件
        backup_files = []
        for date_dir in self.log_dir.glob("*"):
            if date_dir.is_dir() and date_dir.name.isdigit():
                # 获取该日期目录下的相关备份文件
                pattern = f"{self.level_name}_*.log"
                backup_files.extend(date_dir.glob(pattern))
        
        # 按修改时间排序，最新的在前
        backup_files.sort(key=os.path.getmtime, reverse=True)
        
        # 删除超出数量的旧文件
        for old_file in backup_files[self.backupCount:]:
            try:
                os.remove(old_file)
                # 如果目录为空，删除目录
                parent_dir = old_file.parent
                if parent_dir != self.log_dir and not list(parent_dir.iterdir()):
                    parent_dir.rmdir()
            except Exception:
                pass


def setup_logger(name="app", level=logging.DEBUG):
    """
    设置并返回一个配置好的logger实例
    
    Args:
        name: logger名称，默认"app"
        level: 日志级别，默认DEBUG
        
    Returns:
        logging.Logger: 配置好的logger实例
    """
    # 如果logger已存在，直接返回
    if name in _loggers:
        return _loggers[name]
    
    # 创建logger
    logger = logging.getLogger(name)
    logger.setLevel(level)
    logger.propagate = False  # 不传播到父logger
    
    # 清除已有的handlers（避免重复添加）
    logger.handlers.clear()
    
    # 创建格式化器
    formatter = logging.Formatter(LOG_FORMAT, DATE_FORMAT)
    
    # 控制台处理器（仅显示error层级：WARNING、ERROR、CRITICAL）
    console_handler = logging.StreamHandler()
    console_handler.setLevel(logging.WARNING)
    console_handler.setFormatter(formatter)
    logger.addHandler(console_handler)
    
    # 为不同级别创建文件处理器
    levels = {
        'debug': logging.DEBUG,
        'info': logging.INFO,
        'error': logging.WARNING,  # error文件从WARNING级别开始记录
        'sync': logging.INFO,  # sync层级使用INFO级别
    }
    
    for level_name, level_value in levels.items():
        # 创建按大小分割的文件处理器（2MB限制）
        file_handler = SizeRotatingFileHandler(
            log_dir=LOG_DIR,
            level_name=level_name,
            max_bytes=MAX_LOG_SIZE,
            backup_count=30
        )
        file_handler.setLevel(level_value)
        file_handler.setFormatter(formatter)
        
        # 添加过滤器，只记录对应级别的日志
        if level_name == 'debug':
            # debug文件只记录DEBUG级别
            file_handler.addFilter(lambda record: record.levelno == logging.DEBUG)
        elif level_name == 'info':
            # info文件只记录INFO级别（排除sync相关的logger）
            file_handler.addFilter(lambda record: record.levelno == logging.INFO and not record.name.startswith('sync'))
        elif level_name == 'error':
            # error文件记录WARNING、ERROR和CRITICAL级别
            file_handler.addFilter(lambda record: record.levelno >= logging.WARNING)
        elif level_name == 'sync':
            # sync文件只记录来自sync相关logger的INFO级别日志
            file_handler.addFilter(lambda record: record.levelno == logging.INFO and record.name.startswith('sync'))
        
        logger.addHandler(file_handler)
    
    # 缓存logger
    _loggers[name] = logger
    
    return logger


# 创建默认logger实例
default_logger = setup_logger("app")

# 导出logger别名，方便导入使用
logger = default_logger


def get_logger(name="app"):
    """
    获取logger实例
    
    Args:
        name: logger名称，默认"app"
        
    Returns:
        logging.Logger: logger实例
    """
    if name in _loggers:
        return _loggers[name]
    return setup_logger(name)


# 提供便捷的日志函数
def debug(msg, *args, **kwargs):
    """记录DEBUG级别日志"""
    default_logger.debug(msg, *args, **kwargs)


def info(msg, *args, **kwargs):
    """记录INFO级别日志"""
    default_logger.info(msg, *args, **kwargs)


def warning(msg, *args, **kwargs):
    """记录WARNING级别日志"""
    default_logger.warning(msg, *args, **kwargs)


def error(msg, *args, **kwargs):
    """记录ERROR级别日志"""
    default_logger.error(msg, *args, **kwargs)


def critical(msg, *args, **kwargs):
    """记录CRITICAL级别日志"""
    default_logger.critical(msg, *args, **kwargs)


def exception(msg, *args, **kwargs):
    """记录异常信息（ERROR级别，包含堆栈跟踪）"""
    default_logger.exception(msg, *args, **kwargs)


# 使用示例
if __name__ == "__main__":
    # 测试日志功能
    logger = get_logger()
    
    logger.debug("这是一条DEBUG日志")
    logger.info("这是一条INFO日志")
    logger.warning("这是一条WARNING日志")
    logger.error("这是一条ERROR日志")
    logger.critical("这是一条CRITICAL日志")
    
    try:
        1 / 0
    except Exception as e:
        logger.exception("捕获到异常")
    
    # 使用便捷函数
    debug("使用便捷函数记录DEBUG")
    info("使用便捷函数记录INFO")
    warning("使用便捷函数记录WARNING")
    error("使用便捷函数记录ERROR")
